package do

import (
	"database/sql/driver"
	"encoding/json"
	"errors"

	"github.com/shopspring/decimal"
)

const (
	// 默认精度, 超过`MoneyDefaultPrecision`位的会自动四舍五入到 `MoneyDefaultPrecision` 位
	MoneyDefaultPrecision = 6
)

// "钱", 精确表示, 实际根据需要可以输出几位小数
type Money struct {
	dec decimal.Decimal
}

// ToYuan 转换为元（浮点数）
func (m *Money) ToDecimal() decimal.Decimal {
	return m.dec
}

// scale: 精度(小数点后带几位小数, 传-1则使用默认精度:2)
func (m Money) ToYuanString() string {
	return m.ToDecimal().StringFixed(int32(MoneyDefaultPrecision))
}

func MoneyFromString(yuan string) (Money, error) {
	d, err := decimal.NewFromString(yuan)
	return Money{dec: d}, err
}

// String 实现 Stringer 接口
func (m Money) String() string {
	return m.ToYuanString()
}

// Value 实现 driver.Valuer 接口，用于存储到数据库
func (m Money) Value() (driver.Value, error) {
	return m.ToYuanString(), nil
}

// Scan 实现 sql.Scanner 接口，用于从数据库读取
func (m *Money) Scan(value interface{}) error {
	if value == nil {
		*m = Money{
			dec: decimal.New(0, 0),
		}
		return nil
	}

	switch v := value.(type) {
	case int64:
		*m = Money{
			dec: decimal.New(v, 0),
		}
	case float64:
		*m = Money{
			dec: decimal.NewFromFloat(v),
		}
	case []byte:
		s := string(v)
		dm, err := MoneyFromString(s)
		if err != nil {
			return err
		}
		*m = dm
	default:
		return errors.New("不支持的 Money 类型")
	}
	return nil
}

// MarshalJSON 实现 JSON 序列化
func (m Money) MarshalJSON() ([]byte, error) {
	return []byte(m.ToYuanString()), nil
}

// UnmarshalJSON 实现 JSON 反序列化
func (m *Money) UnmarshalJSON(data []byte) error {
	if dm, err := MoneyFromString(string(data)); err == nil {
		*m = dm
		return nil
	}

	// 如果字符串解析失败，尝试解析为数字
	var numVal float64
	if err := json.Unmarshal(data, &numVal); err != nil {
		return errors.New("money 必须是字符串或数字格式")
	}

	*m = Money{dec: decimal.NewFromFloat(numVal)}
	return nil
}
